package game;

import cards.Card;
import static game.Player.CardInfo;

import static org.testng.Assert.*;
import org.testng.annotations.*;
import java.util.*;

public class PokerHandRankTest {
  private static final String[] nothingHand = { "2d", "4s", "5s", "8c", "Jh", "Ts", "6d"};
  private static final String[] pairHand = { "2d", "2s", "5s", "8c", "Jh", "Ts", "6d"};
  private static final String[] twoPairHand = { "2d", "2s", "5s", "5c", "Jh", "Ts", "6d"};
  private static final String[] threeOfAKindHand = { "2d", "2s", "5s", "8c", "Jh", "Ts", "2h"};
  private static final String[] straightHand = { "2d", "2s", "5s", "4c", "3h", "As", "2d"};
  private static final String[] flushHand = { "2d", "2s", "5s", "3c", "Js", "Ts", "6s"};
  private static final String[] fullHouseHand = { "2d", "2s", "5s", "5c", "Jh", "Ts", "2h"};
  private static final String[] fourOfAKindHand = { "2d", "2s", "5s", "8c", "2h", "Ts", "2c"};
  private static final String[] straightFlushHand = { "Td", "2s", "5s", "4s", "3s", "As", "8d"};

  public enum RankTest {
    NOTHING(
        new String[][] { nothingHand, pairHand, twoPairHand, threeOfAKindHand,
          straightHand, flushHand, fullHouseHand, fourOfAKindHand,
          fourOfAKindHand },
        new String[][] { }),
    PAIR(
        new String[][] { pairHand, fullHouseHand, threeOfAKindHand,
          fourOfAKindHand, twoPairHand, flushHand, straightHand },
        new String[][] { nothingHand, straightFlushHand }),
    TWO_PAIR(
        new String[][] { twoPairHand, fullHouseHand, fourOfAKindHand },
        new String[][] { pairHand, nothingHand, straightFlushHand,
          threeOfAKindHand, straightHand, flushHand }),
    THREE_OF_A_KIND(
        new String[][] { threeOfAKindHand, fullHouseHand, fourOfAKindHand },
        new String[][] { pairHand, twoPairHand, nothingHand, straightFlushHand,
          straightHand, flushHand }),
    STRAIGHT(
        new String[][] { straightHand, straightFlushHand },
        new String[][] { nothingHand, pairHand, twoPairHand, threeOfAKindHand,
          flushHand, fullHouseHand, fourOfAKindHand }),
    FLUSH(
        new String[][] { flushHand, straightFlushHand },
        new String[][] { nothingHand, pairHand, twoPairHand, threeOfAKindHand,
          straightHand, fullHouseHand, fourOfAKindHand }),
    FULL_HOUSE(
        new String[][] { fullHouseHand },
        new String[][] { nothingHand, pairHand, twoPairHand, threeOfAKindHand,
          straightHand, flushHand, fourOfAKindHand, straightFlushHand }),
    FOUR_OF_A_KIND(
        new String[][] { fourOfAKindHand },
        new String[][] { nothingHand, pairHand, twoPairHand, threeOfAKindHand,
          straightHand, flushHand, fullHouseHand, straightFlushHand }),
    STRAIGHT_FLUSH(
        new String[][] { straightFlushHand },
        new String[][] { nothingHand, pairHand, twoPairHand, threeOfAKindHand,
          straightHand, flushHand, fullHouseHand, fourOfAKindHand });

    private Card[][] matches;
    private Card[][] nonMatches;
    private HandRank handRank;

    RankTest(String[][] matches, String[][] nonMatches) {
      this.matches = convertToCards(matches);
      this.nonMatches = convertToCards(nonMatches);
      this.handRank = PokerHandRank.valueOf(toString());
    }

    @Test
    public void testNonMatches() {
      for (Card[] cardArray : nonMatches) {
        List<Card> cardList = Arrays.asList(cardArray);
        Set<Card> cards = Collections.unmodifiableSet(
            new HashSet<Card>(cardList));
        assertEquals(handRank.hasRank(cards), false,
            "Rank " + this.toString() + " matched hand unexpectedly: "
            + Card.toString(cardList));
      }
    }

    @Test
    public void testMatches() {
      for (Card[] cardArray : matches) {
        List<Card> cardList = Arrays.asList(cardArray);
        Set<Card> cards = Collections.unmodifiableSet(
            new HashSet<Card>(cardList));
        assertEquals(handRank.hasRank(cards), true,
            "Rank " + this.toString() + " did not match expected hand: "
            + Card.toString(cardList));
      }
    }

    private static Card[][] convertToCards(String[][] cardMatrix) {
      Card[][] ret = new Card[cardMatrix.length][];
      int i = 0;
      for (String[] cardList : cardMatrix) {
        ret[i] = new Card[cardList.length];
        int j = 0;
        for (String card : cardList) {
          ret[i][j] = Card.getCardForString(card);
          assert ret[i][j] != null : "Invalid card";
          j++;
        }
        i++;
      }
      return ret;
    }
  }

  @Test
  public void ensureAllRanksTested() {
    assertEquals(RankTest.values().length, PokerHandRank.values().length,
        "Not all PokerHandRank values are tested.");
  }

  /* TODO(eandres): Find a way to set groups and testNames on this... */
  @Factory
  public RankTest[] testHandRanks() {
    return RankTest.values();
  }
}
